using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Clouds2D : MonoBehaviour
{
    
    public Vector2 speedRange = new (0.001f, 1);
    public int cloudObjCount = 20;
    public Sprite[] sprites;
    public GameObject cloudPrefab;


    public Vector2Int recordTexOrigin = new (-100, -100);
    public Vector2Int recordTexSize = new (500, 500);
    public Vector2 worldHalfGridSize = new(2, 1);


    private Texture2D recordTex;

    public static Vector2 CartesianToAffine(Vector2 cartesianPos, Vector2 origin, Vector2 u, Vector2 v)
    {
        // 计算向量PO = P - O
        Vector2 po = cartesianPos - origin;

        // 计算矩阵行列式
        float det = u.x * v.y - u.y * v.x;

        // 避免除以零（基向量不平行时行列式不为零）
        if (Mathf.Abs(det) < 0.0001f)
        {
            return Vector2.zero;
        }

        // 计算a和b（使用克莱默法则求解线性方程组）
        float a = (po.x * v.y - po.y * v.x) / det;
        float b = (po.y * u.x - po.x * u.y) / det;

        return new Vector2(a, b);
    }

    public void OpenByWorldPos(Vector2 worldPos)
    {
        var gridPos = CartesianToAffine(worldPos, Vector2.zero, worldHalfGridSize, new Vector2(-worldHalfGridSize.x, worldHalfGridSize.y));

        var intGridPos = Vector2Int.zero;
        intGridPos.x = Mathf.FloorToInt(gridPos.x);
        intGridPos.y = Mathf.FloorToInt(gridPos.y);
        OpenByCenter(intGridPos);
    }

    public void OpenByWorldPosFade(Vector2 worldPos, float fadeTime = 1)
    {
        var gridPos = CartesianToAffine(worldPos, Vector2.zero, worldHalfGridSize, new Vector2(-worldHalfGridSize.x, worldHalfGridSize.y));

        var intGridPos = Vector2Int.zero;
        intGridPos.x = Mathf.FloorToInt(gridPos.x);
        intGridPos.y = Mathf.FloorToInt(gridPos.y);
        this.StartCoroutine(OpenByCenterFade(fadeTime, intGridPos));
    }

    public IEnumerator OpenByCenterFade(float fadeTime, Vector2Int gridPos, int sizeX = 1, int sizeY = 0)
    {
        if (sizeY <= 0)
        {
            sizeY = sizeX;
        }

        //if(sizeY <= 0)
        //{
        //    sizeY = sizeX;
        //}

        //for (var i = 0; i < sizeX + 1; i++)
        //{
        //    for (var j = 0; j < sizeY + 1; j++)
        //    {
        //        cloudTex.SetPixel(i + gridPos.x - origin.x - sizeX / 2, j + gridPos.y - origin.y - sizeY / 2, Color.clear);
        //    }
        //}

        //cloudTex.Apply();

        yield return OpenFade(fadeTime, gridPos - new Vector2Int(sizeX - 1, sizeY - 1) / 2, sizeX, sizeY);
    }

    public void OpenByCenter(Vector2Int gridPos, int sizeX = 1, int sizeY = 0)
    {
        if (sizeY <= 0)
        {
            sizeY = sizeX;
        }

        //if(sizeY <= 0)
        //{
        //    sizeY = sizeX;
        //}

        //for (var i = 0; i < sizeX + 1; i++)
        //{
        //    for (var j = 0; j < sizeY + 1; j++)
        //    {
        //        cloudTex.SetPixel(i + gridPos.x - origin.x - sizeX / 2, j + gridPos.y - origin.y - sizeY / 2, Color.clear);
        //    }
        //}

        //cloudTex.Apply();

        Open(gridPos - new Vector2Int(sizeX - 1, sizeY - 1) / 2, sizeX, sizeY);
    }

    public void Open(Vector2Int gridPos, int sizeX = 1, int sizeY = 0)
    {
        if (sizeY <= 0)
        {
            sizeY = sizeX;
        }

        //for (var i = 0; i < sizeX + 1; i++)
        //{
        //    for (var j = 0; j < sizeY + 1; j++)
        //    {
        //        cloudTex.SetPixel(i + gridPos.x - origin.x, j + gridPos.y - origin.y, Color.clear);
        //    }
        //}

        FadeGrid(gridPos, sizeX, sizeY, 1);

        recordTex.Apply();
    }

    public IEnumerator OpenFade(float fadeTime, Vector2Int gridPos, int sizeX = 1, int sizeY = 0)
    {
        if (sizeY <= 0)
        {
            sizeY = sizeX;
        }

        float speed = 1 / fadeTime;

        var startTime = Time.time;
        while (Time.time - startTime <= fadeTime)
        {
            gridPos = FadeGrid(gridPos, sizeX, sizeY, speed * Time.deltaTime);
            recordTex.Apply();

            yield return null;
        }
    }

    private Vector2Int FadeGrid(Vector2Int gridPos, int sizeX, int sizeY, float fadeValue)
    {
        for (var i = 0; i < sizeX + 1; i++)
        {
            for (var j = 0; j < sizeY + 1; j++)
            {
                var c = recordTex.GetPixel(i + gridPos.x - recordTexOrigin.x, j + gridPos.y - recordTexOrigin.y);
                c.a -= fadeValue;
                c.a = Mathf.Max(0, c.a);
                recordTex.SetPixel(i + gridPos.x - recordTexOrigin.x, j + gridPos.y - recordTexOrigin.y, c);
            }
        }

        return gridPos;
    }


    public bool autoCreate = true;
    void Awake()
    {
        if(autoCreate) CreateClouds();
    }

    public void CreateClouds()
    {
        recordTex = new Texture2D(recordTexSize.x, recordTexSize.y, TextureFormat.Alpha8, false, false);
        recordTex.wrapMode = TextureWrapMode.Clamp;
        recordTex.filterMode = FilterMode.Bilinear;

        Color[] cs = new Color[recordTex.width * recordTex.height];

        System.Array.Fill(cs, Color.white);
        recordTex.SetPixels(0, 0, recordTex.width, recordTex.height, cs);

        recordTex.Apply();


        cloudPrefab.SetActive(true);

        for (int i = 0; i < cloudObjCount; i++)
        {
            var r = GameObject.Instantiate(cloudPrefab, this.transform).GetComponent<SpriteRenderer>();
            r.sprite = sprites[Random.Range(0, sprites.Length)];
            var mpb = new MaterialPropertyBlock();
            r.GetPropertyBlock(mpb);

            mpb.SetVector("_Speed", Quaternion.Euler(0, 0, Random.Range(0, 360)) * Vector2.right * Random.Range(speedRange.x, speedRange.y));
            mpb.SetVector("_Offset", new Vector2(Random.Range(0.0f, 1f), Random.Range(0.0f, 1f)));
            mpb.SetTexture("_CloudTex", recordTex);
            mpb.SetFloat("_CloudTexWidth", recordTex.width);
            mpb.SetVector("_CloudTexOrigin", new Vector4(recordTexOrigin.x, recordTexOrigin.y));
            mpb.SetVector("_MainTexSize", new Vector4(r.sprite.bounds.size.x * r.sprite.pixelsPerUnit, r.sprite.bounds.size.y * r.sprite.pixelsPerUnit));

            r.SetPropertyBlock(mpb);
        }

        cloudPrefab.SetActive(false);
    }
}
